namespace OFX.Aggregator.WebScraping.Implementation
{
	#region #using Directives

	using System;
	using System.Diagnostics;
	using System.Runtime.InteropServices;

	using Aggregator.Infrastructure;

	using Infrastructure;

	#endregion

	public class WebStatementRetrievalAsyncResult : Infrastructure.WebStatementRetrievalAsyncResult
	{
		public WebStatementRetrievalAsyncResult(AsyncCallback callback, IStatementProcessingWorkflow workflow, string initialAddress,
		                                        IHtmlPageProcessor[] htmlProcessors, IProgressReport progressReport, IWebBrowser webBrowser)
			: base(callback, workflow, htmlProcessors)
		{
			this.statementSource = workflow.StatementSource;
			this.progressReport = progressReport;
			this.webBrowser = webBrowser;

			if (progressReport != null)
			{
				progressReport.OnProgress(CurrentStageIndex, HtmlProcessors.Length);
			}

			this.webBrowser.Navigate(initialAddress);
			this.webBrowser.DocumentCompleted += webBrowser_DocumentCompleted;

			if (this.webBrowser.ReadyState == WebBrowserReadyState.Complete)
			{
				ProcessPage(this.webBrowser);
			}
		}

		#region Overrides

		public override Exception Exception
		{
			protected set
			{
				if (value == null)
				{
					throw new ArgumentNullException("value");
				}

				base.Exception = value;

				if (this.progressReport != null)
				{
					this.progressReport.ReportError(value);
				}

				var disposable = this.webBrowser as IDisposable;
				if (disposable != null)
				{
					disposable.Dispose();
				}
			}
		}

		/// <summary>
		/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
		/// </summary>
		/// <filterpriority>2</filterpriority>
		protected override void Dispose(bool disposing)
		{
			if (disposing)
			{
				var disposable = this.webBrowser as IDisposable;
				if (disposable != null)
				{
					disposable.Dispose();
				}
			}

			base.Dispose(disposing);
		}

		#endregion

		#region Private Methods

		private void ProcessPage(IWebBrowser browser)
		{
			var processor = CurrentProcessor;

			if (processor == null)
			{
				return;
			}

			try
			{
				processor.TestAssumptions(this.statementSource, browser);
				processor.ProcessPage(this.statementSource, browser, this);

				++CurrentStageIndex;

				if (this.progressReport != null)
				{
					this.progressReport.OnProgress(CurrentStageIndex, HtmlProcessors.Length);
				}

				if (CurrentProcessor == null)
				{
					browser.DocumentCompleted -= webBrowser_DocumentCompleted;

					if (this.progressReport != null)
					{
						this.progressReport.OnFinish();
					}
				}
			}
			catch (ArgumentException argumentEx)
			{
				HandleException(argumentEx);
			}
			catch (AssumptionFailedException assumptionFailedEx)
			{
				HandleException(assumptionFailedEx);
			}
			catch (COMException comEx)
			{
				HandleException(comEx);
			}
		}

		#endregion

		#region Private Fields

		private readonly IProgressReport progressReport;
		private readonly StatementSource statementSource;
		private readonly IWebBrowser webBrowser;

		#endregion

		#region Event Handlers

		private void webBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
		{
			if (this.webBrowser.ReadyState == WebBrowserReadyState.Complete)
			{
				Debug.Assert(this.webBrowser.Document != null);

				ProcessPage(this.webBrowser);
			}
		}

		#endregion
	}
}